/*
 * Copyright (c) 2018-2999 深圳市阿毛科技有限公司 All rights reserved.
 *
 * https://www.lamyz.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */

package com.lam.mall.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.lam.common.constant.MallStateConstant;
import com.lam.common.exception.BaseException;
import com.lam.common.exception.BusinessException;
import com.lam.common.pay.service.LamAlipayService;
import com.lam.common.pay.service.LamWxPayService;
import com.lam.common.pay.vo.UnifiedOrderVo;
import com.lam.common.utils.CollectionUtil;
import com.lam.common.utils.NumberUtil;
import com.lam.common.utils.StringUtils;
import com.lam.common.utils.uuid.IdUtils;
import com.lam.mall.app.param.PayParam;
import com.lam.mall.app.vo.PayDataVo;
import com.lam.mall.common.event.PaySuccessOrderEvent;
import com.lam.mall.entity.OrderLog;
import com.lam.mall.entity.OrderSettlement;
import com.lam.mall.entity.PayInfo;
import com.lam.mall.enums.PayStatus;
import com.lam.mall.enums.PayType;
import com.lam.mall.mapper.OrderLogMapper;
import com.lam.mall.mapper.OrderMapper;
import com.lam.mall.mapper.OrderSettlementMapper;
import com.lam.mall.mapper.PayInfoMapper;
import com.lam.mall.service.PayService;

import cn.hutool.core.lang.Snowflake;

/**
 */
@Service
public class PayServiceImpl implements PayService {
	
	 private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderLogMapper orderLogMapper;
    @Autowired
    private OrderSettlementMapper orderSettlementMapper;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private PayInfoMapper payInfoMapper;
    @Autowired
	private LamWxPayService lamWxPayService;
	@Autowired
	private LamAlipayService lamAlipayService;
	
    @Override
	public PayInfo selectByPayNo(String payNo) {
		return payInfoMapper.selectById(payNo);
	}
    
    /**
     * 不同的订单号，同一个支付流水号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public PayDataVo pay(String userId, PayParam payParam) {

        // 不同的订单号的产品名称
//        List<String> prodNameList = new ArrayList<>();
        // 支付单号
        String payNo = "py" + String.valueOf(snowflake.nextId());
        List<String> orderNumbers = StringUtils.str2List(payParam.getOrderNumbers());
        
    	List<OrderSettlement> settlements = orderSettlementMapper.selectByOrderNumberAndUserId(orderNumbers, userId);
    	if(CollectionUtil.isEmpty(settlements)) {
    		logger.error("支付订单时检查到不存在的订单结算信息，orderNumber：{}", orderNumbers);
        	throw new BusinessException("订单信息不存在！");
    	}
    	Set<Object> orderNumberSet = CollectionUtil.getPropertySet(settlements, t->t.getOrderNumber());
    	for (String od : orderNumbers) {
    		if(!orderNumberSet.contains(od)) {
    			logger.error("支付订单时检查到不存在的订单结算信息，orderNumber：{}", od);
            	throw new BusinessException("订单信息不存在！");
    		}
		}
    	
    	// 应支付的总金额
        BigDecimal payAmount = BigDecimal.ZERO;
        for (OrderSettlement settlement : settlements) {
        	if(!StringUtils.equals(settlement.getUserId(), userId)) {
            	logger.error("支付订单时检查到订单的userId不一致，orderNumber：{}, orderUserId:{}, userId:{}", settlement.getOrderNumber(), settlement.getUserId(), userId);
            	throw new BusinessException("订单信息不一致！");
            }
            payAmount = NumberUtil.add(payAmount, settlement.getPayAmount());
        }
        
        OrderLog orderLog = new OrderLog();
    	orderLog.setCreateBy(userId);
    	orderLog.setCreateTime(new Date());
    	orderLog.setOperType(MallStateConstant.ORDER_OPER_TYPE_BUYER);
    	
    	String payInfoBody = "热潮严选商品";
    	String payTypeName = PayType.instance(payParam.getPayType()).getName();
        UnifiedOrderVo unifiedOrderVo = null;
        try {
			if(NumberUtil.eq(PayType.WXPAY.code(), payParam.getPayType())) {
				unifiedOrderVo = lamWxPayService.createWxH5Order(payNo, payAmount, payInfoBody, payParam.getOrderNumbers());
			}else if(NumberUtil.eq(PayType.ALIPAY.code(), payParam.getPayType())) {
				unifiedOrderVo = lamAlipayService.createAlipayH5Order(payNo, payAmount, payInfoBody, payParam.getOrderNumbers());
			}else if(NumberUtil.eq(PayType.MONI.code(), payParam.getPayType())) {
				unifiedOrderVo = new UnifiedOrderVo(IdUtils.getUid(), "");//模拟支付
	        }else {
				throw new BaseException("不支持的支付方式");
			}
		} catch (Exception e) {
			if(e instanceof BaseException) {
				throw e;
			}
			logger.error("支付订单时H5统一下单失败", e);
			// 修改订单信息
	        for (OrderSettlement settlement : settlements) {
	            orderLog.setId(snowflake.nextIdStr());
	        	orderLog.setOrderNumber(settlement.getOrderNumber());
	        	orderLog.setOperRemark(String.format("用户发起(%s)支付订单失败！", payTypeName));
				orderLogMapper.insert(orderLog);
	        }
	        throw e;
		}

        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payNo);
        payInfo.setBizPayNo(unifiedOrderVo.getTradeNo());
        payInfo.setUserId(userId);
        payInfo.setPayAmount(payAmount);
        payInfo.setPayStatus(PayStatus.UNPAY.value());
        payInfo.setPayType(payParam.getPayType());
        payInfo.setVersion(0);
        payInfo.setOrderIds(payParam.getOrderNumbers());// 保存多个支付订单号
        payInfoMapper.insert(payInfo);// 保存预支付信息
        
        OrderSettlement newSettlement = new OrderSettlement();
        newSettlement.setPayNo(payNo);
        newSettlement.setBizPayNo(unifiedOrderVo.getTradeNo());
        newSettlement.setPayType(payParam.getPayType());
        // 修改订单信息
        for (OrderSettlement settlement : settlements) {
        	
            newSettlement.setSettlementId(settlement.getSettlementId());
            orderSettlementMapper.updateById(newSettlement);
            
            orderLog.setId(snowflake.nextIdStr());
        	orderLog.setOrderNumber(settlement.getOrderNumber());
        	orderLog.setOperRemark(String.format("用户发起(%s)支付订单！", payTypeName));
			orderLogMapper.insert(orderLog);
        }

        return new PayDataVo(payParam.getPayType(), payNo, unifiedOrderVo.getTradeNo(), unifiedOrderVo.getPayFormData());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(String payNo, String bizPayNo, PayType payType) {
    	
    	PayInfo payInfo = payInfoMapper.selectById(payNo);
    	if(payInfo == null) {
    		logger.error("支付结果确认时，订单支付信息不存在, payNo:{}", payNo);
            throw new BusinessException("订单状态异常！");
    	}
    	
    	if(NumberUtil.eq(payInfo.getPayStatus(), PayStatus.PAYED.value())) {
    		logger.warn("支付结果确认时，订单支付信息状态为已支付, payNo:{}", payNo);
            return ;
    	}
    	
        List<OrderSettlement> orderSettlements = orderSettlementMapper.selectList(new LambdaQueryWrapper<OrderSettlement>().eq(OrderSettlement::getPayNo, payNo));
        OrderSettlement settlement = orderSettlements.get(0);
        // 订单已支付
        if(NumberUtil.eq(settlement.getPayStatus(), PayStatus.PAYED.value())) {
            logger.warn("支付结果确认时，订单结算信息为已支付, payNo:{}", payNo);
            return ;
        }
        
        if(payInfoMapper.updateToPay(payNo, bizPayNo, payType.code(), payInfo.getVersion()) < 1) {
        	logger.warn("支付结果确认时，订单支付信息已更改, payNo:{}", payNo);
            return ;
        }
        // 修改订单结算信息
        if (orderSettlementMapper.updateToPay(payNo, bizPayNo, payType.code(), payType.getName(), settlement.getVersion()) < 1) {
            logger.warn("支付结果确认时，订单结算信息已更改, payNo:{}", payNo);
            return ;
        }
        List<String> orderNumbers = orderSettlements.stream().map(OrderSettlement::getOrderNumber).collect(Collectors.toList());
        // 将订单改为已支付状态
        orderMapper.updateByToPaySuccess(orderNumbers, payType.code(), payInfo.getPayAmount());
        
        OrderLog orderLog = new OrderLog();
    	orderLog.setCreateBy("system");
    	orderLog.setCreateTime(new Date());
    	orderLog.setOperType(MallStateConstant.ORDER_OPER_TYPE_SYS);
    	orderLog.setOperRemark("订单已确认支付！");
    	for (OrderSettlement orderSettlement : orderSettlements) {
    		orderLog.setId(snowflake.nextIdStr());
        	orderLog.setOrderNumber(orderSettlement.getOrderNumber());
    		orderLogMapper.insert(orderLog);
		}
    	logger.info("订单支付结果已确认，支付成功！, payNo:{}", payNo);
    	
        eventPublisher.publishEvent(new PaySuccessOrderEvent(orderNumbers));
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean queryPayResult(String payNo, String bizPayNo, Integer payType) {
    	
    	if(NumberUtil.eq(PayType.WXPAY.code(), payType)) {
        	WxPayOrderQueryResult result = lamWxPayService.queryTradeResult(payNo, bizPayNo);
        	String tradeStatus = result.getTradeState();
        	if (StringUtils.equalsAny(tradeStatus, "SUCCESS")) {//支付成功
				paySuccess(payNo, result.getTransactionId(), PayType.WXPAY);
				return false;
        	}
        	return false;
        }
        
        if(NumberUtil.eq(PayType.ALIPAY.code(), payType)) {
        	AlipayTradeQueryResponse response = lamAlipayService.queryTradeResult(payNo, bizPayNo);
        	String tradeStatus = response.getTradeStatus();
        	if (StringUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS")) {
        		//交易完成，不可退款, //支付成功，可以退款
				paySuccess(payNo, response.getTradeNo(), PayType.ALIPAY);
				return true;
        	}
        	return false;
        }
        return false;
    }

}
